#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <util/delay.h>
#include <stdint.h>
#include "common.h"
#include "delay_util.h"
#include "main.h"
#include "descriptors.h"
#include "pn.h"
#include "card.h"
#include "led.h"
#include "CDCClassDevice.h"
#include "bootloader_support.h"

#include "klPrintf_USB.h"


bool USB_IsConnected;

// Clock division
typedef enum {
    clock_div_1 = 0,
    clock_div_2 = 1,
    clock_div_4 = 2,
    clock_div_8 = 3,
    clock_div_16 = 4,
    clock_div_32 = 5,
    clock_div_64 = 6,
    clock_div_128 = 7,
    clock_div_256 = 8,
} clock_div_t;

// Set the clock prescaler register select bits, selecting a system clock division setting. They type of x is clock_div_t.
#define clock_prescale_set(x) \
do { \
    uint8_t tmp = _BV(CLKPCE); \
    __asm__ __volatile__ ( \
        "in __tmp_reg__,__SREG__" "\n\t" \
        "cli" "\n\t" \
        "sts %1, %0" "\n\t" \
        "sts %1, %2" "\n\t" \
        "out __SREG__, __tmp_reg__" \
        : /* no outputs */ \
        : "d" (tmp), \
          "M" (_SFR_MEM_ADDR(CLKPR)), \
          "d" (x) \
        : "r0"); \
} while (0)

// =========================== Implementation ==================================
int main(void) {
    _delay_ms(99);
    // ==== Init ====
    clock_prescale_set(clock_div_1);    // Disable clock division
    // ==== Comparator ==
    // === Check which power supply used ===
    // Enable comparator with bandgap on positive
    ACSR = (0<<ACD)|(1<<ACBG);
    ACMUX = 0x01;   // AIN2 == PC2
    // Wait for reference to start
    _delay_ms(4);
    //DIDR1 |= 0x04;
    // Check comparator output
    if(bit_is_set(ACSR, ACO)) {
        klPrintf("\rEntering bootloader\r");
        StartBootloader();
    }
    // Disable comparator
    ACSR = (1<<ACD);


    DelayInit();

    USB_Init();

    Pn.Init();

    sei();
    klPrintf("RJ Metro CdReader v.%u\r", CURRENT_FIRMWARE_VERSION);

    // ==== Main cycle ====
    while(1) {
        USB_USBTask();
        CDC_Device_ReceiveByte();
        CDC_Device_USBTask();

        Pn.Task();
        Pn.LedTask();                  // Automatically PN Task LED
//        Led.Task();                   // Manually switch blue LED
    }
    return 0;
}

// ========================== USB events handlers ==============================
/** Event handler for the USB_Connect event. This indicates that the device is enumerating via the status LEDs and
 *  starts the library USB task to begin the enumeration and USB management process. */
void EVENT_USB_Device_Connect() {
    Led.Init();
    Led.RedOn();
}

/** Event handler for the USB_Disconnect event. This indicates that the device is no longer connected to a host via
 *  the status LEDs and stops the USB management and CDC management tasks. */
void EVENT_USB_Device_Disconnect() {
    Led.Deinit();
    USB_IsConnected = false;
}

/** Event handler for the USB_ConfigurationChanged event. This is fired when the host set the current configuration
 *  of the USB device after enumeration - the device endpoints are configured and the CDC management task started. */
void EVENT_USB_Device_ConfigurationChanged() {
    // Setup Endpoints
    Endpoint_ConfigureEndpoint(CDC_RX_EPNUM, EP_TYPE_BULK, ENDPOINT_DIR_OUT, CDC_TXRX_EPSIZE, ENDPOINT_BANK_SINGLE);
    Endpoint_ConfigureEndpoint(CDC_TX_EPNUM, EP_TYPE_BULK, ENDPOINT_DIR_IN,  CDC_TXRX_EPSIZE, ENDPOINT_BANK_SINGLE);
    Endpoint_ConfigureEndpoint(CDC_NOTIFICATION_EPNUM, EP_TYPE_INTERRUPT, ENDPOINT_DIR_IN,  CDC_NOTIFICATION_EPSIZE, ENDPOINT_BANK_SINGLE);
}

/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void) {
    CDC_Device_ProcessControlRequest();
}
